package fr.grassaud.hmvc.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import fr.grassaud.hmvc.component.Child;
import fr.grassaud.hmvc.component.ControlledModel;
import fr.grassaud.hmvc.component.ControlledView;
import fr.grassaud.hmvc.component.Controller;
import fr.grassaud.hmvc.component.Model;
import fr.grassaud.hmvc.component.View;
import fr.grassaud.hmvc.event.HMVCEvent;
import fr.grassaud.hmvc.event.OnInit;
import fr.grassaud.hmvc.event.OnRelease;
import fr.grassaud.hmvc.info.TriadInfo;
import fr.grassaud.hmvc.session.Triad;

public class HMVCIntrospector {

    private HMVCIntrospector() {
	// avoid instantiation
    }

    static public boolean isViewComponent(Object object) {
	return isAnnotatedBy(object, View.class);
    }

    static public boolean isControllerComponent(Object object) {
	return isAnnotatedBy(object, Controller.class);
    }

    static public boolean isModelComponent(Object object) {
	return isAnnotatedBy(object, Model.class);
    }

    private static boolean isAnnotatedBy(Object object, Class<? extends Annotation> annotationClass) {
	if (object == null) {
	    throw new IllegalArgumentException("the object cannot be null");
	}

	Class<?> type = null;
	if (object instanceof Class) {
	    type = (Class<?>) object;
	} else {
	    type = object.getClass();
	}

	return type.isAnnotationPresent(annotationClass);
    }

    /**
     * 
     * @param clazz
     * @return
     */
    public static List<Field> getAllFields(Class<?> clazz) {
	List<Field> fields = new ArrayList<Field>();
	for (Field field : clazz.getDeclaredFields()) {
	    fields.add(field);
	}
	Class<?> superClass = clazz.getSuperclass();
	if (superClass != null) {
	    fields.addAll(getAllFields(superClass));
	}
	return fields;
    }

    public static Field getField(Class<?> clazz, String name) throws SecurityException {
	Field field = null;
	try {
	     field = clazz.getDeclaredField(name);
	} catch (NoSuchFieldException e) {
           field = null;
	}
	if (field == null) {
	    Class<?> superClass = clazz.getSuperclass();
	    field = getField(superClass, name);
	}

	return field;
    }

    /**
     * 
     * @param clazz
     * @return
     */
    public static List<Method> getAllMethods(Class<?> clazz) {
	List<Method> result = new ArrayList<Method>();
	for (Method m : clazz.getDeclaredMethods()) {
	    result.add(m);
	}
	Class<?> superClass = clazz.getSuperclass();
	if (superClass != null) {
	    result.addAll(getAllMethods(superClass));
	}
	return result;
    }

    public static boolean isChild(Field f) {
	return f.isAnnotationPresent(Child.class);
    }

    public static boolean isView(Field f) {
	return f.isAnnotationPresent(ControlledView.class);
    }

    public static boolean isModel(Field f) {
	return f.isAnnotationPresent(ControlledModel.class);
    }

    public static boolean isInitMethod(Method m) {
	return m.isAnnotationPresent(OnInit.class);
    }
    
    public static boolean isReleaseMethod(Method m) {
   	return m.isAnnotationPresent(OnRelease.class);
       }

    public static Class<?> getViewController(Class<?> viewClass) {
	View annotation = viewClass.getAnnotation(View.class);
	return annotation.controlledBy();
    }

    public static Class<?> getModelController(Class<?> modelClass) {
	Model annotation = modelClass.getAnnotation(Model.class);
	return annotation.controlledBy();
    }

    public static Triad getChildWithEvent(HMVCEvent event, Triad parent) {
	Triad result = null;
	Map<String, Triad> children = parent.getChildren();
	if (children != null) {

	    for (Iterator<String> names = children.keySet().iterator(); names.hasNext() && result == null;) {
		String name = names.next();
		Triad triad = children.get(name);
		TriadInfo info = triad.getTriadInfo();
		if (info.getControllerEventMethod(event.getName()) != null) {
		    result = triad;
		    break;
		}
		result = getChildWithEvent(event, triad);
	    }
	}
	return result;
    }

    public static Triad getParentWithEvent(HMVCEvent event, Triad triad) {
	Triad result = null;
	Triad parent = triad.getParent();
	if (parent != null) {
	  
	    if (  parent.canConsumeEvent(event)) {
		result = parent;
	    } else {
		result = getParentWithEvent(event, parent);
	    }

	}

	return result;
    }

    static public Class<?> getControllerClass(Object component) {
	Class<?> result = null;
	Class<?> componentClass = component.getClass();
	if (componentClass.isAnnotationPresent(Controller.class)) {
	    result = componentClass;
	} else if (componentClass.isAnnotationPresent(Model.class)) {
	    Model annotation = componentClass.getAnnotation(Model.class);
	    result = annotation.controlledBy();
	} else if (componentClass.isAnnotationPresent(View.class)) {
	    View annotation = componentClass.getAnnotation(View.class);
	    result = annotation.controlledBy();
	}
	return result;
    }

    public static String computeChildName(String prefix, Class<?> controllerClass) {
	StringBuilder builder = new StringBuilder();
	builder.append(prefix).append(".").append(controllerClass.getName());
	return builder.toString();
    }
    
    public static String computeChildName(int id,String prefix, Class<?> controllerClass) {
   	
   	return id + ":" + computeChildName(prefix,controllerClass);
       }
    
    
    static public boolean isCreatedChild(Method m ) {
	Annotation[][] annotations = m.getParameterAnnotations();
	for ( Annotation[] paramAnnotation : annotations) {
	    for ( Annotation anno : paramAnnotation ) {
		if ( anno.annotationType().equals(Child.class)) {
		    return true;
		}
	    }
	}
	return false;
    }

  }
